#region Copyright
/**
 *  This file is part of the Onix Solutions .NET FIX Engine Code Samples.
 *  
 *   Copyright (C) 2005-2012 Onix Solutions [OnixS].  All rights reserved.
 *  
 * This source code is intended only as a supplement to Onix Solutions
 * .NET FIX Engine and/or documentation. See these other materials 
 * for detailed information regarding Onix Solutions code samples.
 *  
 * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 */
#endregion

using System;
using System.Diagnostics;

using FIXForge.NET.FIX;
using FIXForge.NET.FIX.FIX43;
using Onixs.FixControls;
using System.Globalization;
using System.Collections.Generic;
using System.Threading;

namespace OMS
{
    public class TradingManager : IDisposable
    {
        private Session session;
		private SessionConfiguration settings;
		public OrderBook orderBook;

        public long orderIDGen = 10000000;

        public delegate void OrderTimerExpiredHandler(Order order);
        public event OrderTimerExpiredHandler OnOrderTimerExpiredHandler;

        bool exitOrderProcessingThread = false;
        Queue<Order> queue = new Queue<Order>();
        private Thread orderProcessingThread = null;
        

        public TradingManager(SessionConfiguration settings)
        {
			this.settings = settings;

			//Debug.Assert(Engine.IsInitialized());

            orderBook = new OrderBook();
			session = new Session(settings.SenderCompID, settings.TargetCompID, settings.Version, settings.KeepSequenceNumbersAfterLogout);
			if (settings.UseSslEncryption)
				session.Encryption = Session.EncryptionMethod.SSL;
			else
				session.Encryption = Session.EncryptionMethod.NONE;
			if (!string.IsNullOrEmpty(settings.SslCertificateFile))
			{
				session.Ssl.CertificateFile = settings.SslCertificateFile;
				session.Ssl.PrivateKeyFile = settings.SslCertificateFile;
			}
			session.InboundApplicationMsgEvent += new Session.InboundApplicationMsgEventHandler(OnInboundApplicationMsgEvent);
		}
		
		public Session Session
        {
            get
            {
                return session;
            }
		}

		/// <summary>
        /// Establishes the FIX Session.
        /// </summary>
        public void Connect()
        {

            // A custom Logon message is only needed if some non-standard fields should to be used in the initial Logon(A) message, 
            // otherwise a simpler overload of Session.LogonAsInitiator method could be used.
            Message customLogon = new Message(MsgType.Logon, session.Version);

            if (!string.IsNullOrEmpty(settings.Username))
            {
				customLogon.Set(Tags.Username, settings.Username);
            }

			if (!string.IsNullOrEmpty(settings.Password))
            {
				customLogon.Set(Tags.Password, settings.Password);
            }

            if (!string.IsNullOrEmpty(settings.ClientID))
            {
				customLogon.Set(Tags.ClientID, settings.ClientID);
            }

            if (!string.IsNullOrEmpty(settings.RawData))
            {
				customLogon.Set(Tags.RawData, settings.RawData);
				customLogon.Set(Tags.RawDataLength, settings.RawData.Length);
            }

			session.LogonAsInitiator(settings.Host, settings.Port, settings.HeartbeatInterval, customLogon, settings.SetResetSeqNumFlag);

            ThreadStart orderThread = new ThreadStart(MonitorOrderStatus);

            if (orderThread != null)
            {
                orderProcessingThread = new Thread(orderThread);
                orderProcessingThread.Name = "Order Process Thread";
                orderProcessingThread.SetApartmentState(ApartmentState.MTA);
                orderProcessingThread.Priority = ThreadPriority.Normal;
                orderProcessingThread.Start();
            }
        }

        public void Disconnect()
        {
            session.Logout("Trading Client initiated the termination of the FIX Connection");
        }

		public string GetNextClientOrderID()
		{
            orderIDGen++;
            return orderIDGen.ToString();
		}

		public bool IsClientOrderIdUnique(string clientOrderID)
		{
			return (null == orderBook.FindOrderByClientOrderID(clientOrderID));
		}

		public void SendNewOrder(Order order)
		{
            order.TransactTime = DateTime.Now;
			Message message = new Message(MsgType.Order_Single, session.Version);

			message[Tags.ClOrdID] = order.ClientOrderID;
			message[Tags.HandlInst] = HandlInst.Automated_execution_order_private_no_Broker_intervention;
			message[Tags.Symbol] = order.Symbol;
			message.Set(Tags.Side, (int)order.Side);
			message[Tags.TransactTime] = order.TransactTime.ToString("yyyyMMdd-HH:mm:ss.fff", CultureInfo.InvariantCulture);
			message.Set(Tags.OrdType, Char.ToString((char)order.Type));

			if (Order.OrderType.Market != order.Type
				&& Order.OrderType.ForeignExchangeMarketOrder != order.Type
				)
			{
				message.Set(Tags.Price, order.Price);
			}

			message.Set(Tags.OrderQty, order.Quantity);


			if (!string.IsNullOrEmpty(order.Currency))
			{
				message.Set(Tags.Currency, order.Currency);
			}

			message.Set(Tags.SettlmntTyp, SettlmntTyp.Regular);
			message.Set(Tags.MaxFloor, 0);
			message.Set(Tags.TimeInForce, TimeInForce.Day);

			if (session.Version < ProtocolVersion.FIX43)
			{
				message.Set(Tags.Rule80A, Rule80A.Individual_Investor_single_order);
			}

			if (!string.IsNullOrEmpty(settings.Account))
			{
				message[Tags.Account] = settings.Account;
			}

			if (!string.IsNullOrEmpty(order.SecurityID))
			{
				message[Tags.SecurityID] = order.SecurityID;
			}

			if (!string.IsNullOrEmpty(order.Text))
			{
				message[Tags.Text] = order.Text;
			}            
            
            session.Send(message);
            order.Status = Order.OrderStatus.New;// remove while going live
            System.Diagnostics.Trace.WriteLine("MBX Update: New Order Sent" + ";" + order.tradeID + ";" + order.ClientOrderID + ";" + order.Status + ";" + DateTime.Now + ";" + order.quantity + ";" + order.filledQuantity + ";" + order.Symbol + ";" + order.Price + ";" + order.Side + ";" + order.ToString());
            orderBook.AddOrder(order);
          //  System.Diagnostics.Trace.WriteLine("ANALYSIS : NEW " + order.tradeID + " : " + order.Side + " : " + order.Price);
            // for testing purpose            
		}

        public void ModifyOrder(Order order)
        {
            order.TransactTime = DateTime.Now;
            Message request = new Message(MsgType.Order_Cancel_Replace_Request, session.Version);

            if (!string.IsNullOrEmpty(settings.Account))
            {
                request[Tags.Account] = settings.Account;
            }

            request[Tags.ClOrdID] = GetNextClientOrderID(); // check. not required.
            request[Tags.OrigClOrdID] = order.ClientOrderID;
            request[Tags.OrderID] = order.OrderID;
            request[Tags.Symbol] = order.Symbol;
            request.Set(Tags.Side, (int)order.Side);
            request.Set(Tags.OrderQty, order.Quantity);
            request[Tags.TransactTime] = DateTime.Now.ToString("yyyyMMdd-HH:mm:ss", CultureInfo.InvariantCulture);
            request.Set(Tags.OrdType, Char.ToString((char)order.Type));

            if (Order.OrderType.Market != order.Type
               && Order.OrderType.ForeignExchangeMarketOrder != order.Type
               )
            {
                request.Set(Tags.Price, order.Price);
            }

            if (session.Version < ProtocolVersion.FIX43)
            {
                request.Set(Tags.Rule80A, Rule80A.Individual_Investor_single_order);
            }

            if (!string.IsNullOrEmpty(order.SecurityID))
            {
                request[Tags.SecurityID] = order.SecurityID;
            }

            if (!string.IsNullOrEmpty(order.Text))
            {
                request[Tags.Text] = order.Text;
            }

            order.Status = Order.OrderStatus.PendingReplace;

            session.Send(request);
            System.Diagnostics.Trace.WriteLine("MBX Update: Modify Request" + ";" + order.tradeID + ";" + order.ClientOrderID + ";" + order.Status + ";" + DateTime.Now + ";" + order.quantity + ";" + order.filledQuantity + ";" + order.Symbol + ";" + order.Price + ";" + order.Side + ";" + order.ToString());
            if (order.filledQuantity == 0)
            {
                order.Status = Order.OrderStatus.Replace; // added by MS for testing - to be removed before going live
            }
            System.Diagnostics.Trace.WriteLine("MBX Update: Modified" + ";" + order.tradeID + ";" + order.ClientOrderID + ";" + order.Status + ";" + DateTime.Now + ";" + order.quantity + ";" + order.filledQuantity + ";" + order.Symbol + ";" + order.Price + ";" + order.Side + ";" + order.ToString());

            //System.Diagnostics.Trace.WriteLine("ANALYSIS : MOD " + order.tradeID + " : " + order.Side + " : " + order.Price);
        }

        public void CancelOrder(Order order) // check
        {
            order.TransactTime = DateTime.Now;
            Message request = new Message(MsgType.Order_Cancel_Request, session.Version);

            if (!string.IsNullOrEmpty(settings.Account))
            {
                request[Tags.Account] = settings.Account;
            }

            request[Tags.ClOrdID] = GetNextClientOrderID(); // check. not required.
            request[Tags.OrigClOrdID] = order.ClientOrderID;
            request[Tags.OrderID] = order.OrderID;
            request[Tags.Symbol] = order.Symbol;
            request.Set(Tags.Side, (int)order.Side);
            request.Set(Tags.OrderQty, order.Quantity);
            request[Tags.TransactTime] = DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss", CultureInfo.InvariantCulture);

            order.Status = Order.OrderStatus.PendingCancel;

            session.Send(request);
            System.Diagnostics.Trace.WriteLine("MBX Update: Cancel Request" + ";" + order.tradeID + ";" + order.ClientOrderID + ";" + order.Status + ";" + DateTime.Now + ";" + order.quantity + ";" + order.filledQuantity + ";" + order.Symbol + ";" + order.Price + ";" + order.Side + ";" + order.ToString());
            if (order.filledQuantity == 0)
            {
                order.Status = Order.OrderStatus.Canceled; // added by MS for testing - to be removed before going live
            }
            System.Diagnostics.Trace.WriteLine("MBX Update: Canceled" + ";" + order.tradeID + ";" + order.ClientOrderID + ";" + order.Status + ";" + DateTime.Now + ";" + order.quantity + ";" + order.filledQuantity + ";" + order.Symbol + ";" + order.Price + ";" + order.Side + ";" + order.ToString());
        }


		public event EventHandler<OrderStateChangedArgs> OrderStateChanged;

        public class OrderStateChangedArgs : EventArgs
        {
            public OrderStateChangedArgs(Order order)
            {
                this.order = order;
            }

            public Order Order
            {
                get
                {
                    return order;
                }
            }

            private Order order;
        }

        private void OnInboundApplicationMsgEvent(object sender, Session.InboundApplicationMsgEventArgs args)
        {
            try
            {
                if (MsgType.Execution_Report != args.Msg.Type)
                {
                    return;
                }
                Order order = orderBook.UpdateOrder(args.Msg);// - commented by MS for testing
                if (null != order)
                {
                    OrderStateChanged(this, new OrderStateChangedArgs(order));// - commented by MS for testing
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError("Exception during the processing of incoming FIX message: " + ex);
            }
        }

        [MTAThread]
        private void MonitorOrderStatus()
        {
            try
            {
                /*
                TimeSpan difference = new TimeSpan();

                Order order = new Order();

                while (exitOrderProcessingThread == false)
                {
                    Thread.Sleep(0);
                    
                    
                    foreach (var item in orderBook.clientOrderID2order)
                    {
                        order = item.Value;

                        if (order.Status == Order.OrderStatus.New || order.Status == Order.OrderStatus.PendingNew || order.Status == Order.OrderStatus.Replace || order.Status == Order.OrderStatus.PendingReplace)
                        {
                            difference = DateTime.Now.Subtract(order.TransactTime);
                            order.workingAge = difference.TotalMilliseconds;

                            if (difference.TotalMilliseconds > 100)
                            {
                                if (OnOrderTimerExpiredHandler != null)
                                {
                                    //OnOrderTimerExpiredHandler(order);
                                    //System.Diagnostics.Trace.WriteLine("MBX Update: Time expired for " + order.SecurityID);
                                }
                            }
                        }
                        else if (order.Status == Order.OrderStatus.PartialFill)
                        {
                            difference = DateTime.Now.Subtract(order.TransactTime);
                            order.partialFillAge = difference.TotalMilliseconds;
                        }
                    }
                   
                }
                 * */
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            }
        

		#region IDisposable Members

		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (session != null)
				{
					session.Dispose();
					session = null;
				}
			}
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		#endregion
	}
}
